#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <time.h>
#include "util.h"


void half_thrash_order(size_t* order, size_t size){
    thrash_order(order, size/2);
    for (size_t i = size/2-1; i <= size/2; i--)
    {
        order[i*2+1] = order[i]*2;
        order[i*2] = order[i]*2 + 1;
    }
    if(size & 1) order[size-1] = size-1;
    for(size_t i = 0; i < size; i++){
        order[i] = order[i] ? order[i] - 1 : size - 1;
    }
    return;
}

int main(int argc, char** argv) {

    void* buffer = alloc_hp();
    set_seed();

    struct timespec begin, end;
    void *p;
    size_t iter, len;
    for (size_t size = 4096; size <= 1ull<<27; size = next_size(size, 1))
    {
        printf("%6ldK ", size/1024);
        for (size_t stride = 16; stride <= 128; stride<<=1)
        {
            len = fill_buf(buffer, half_thrash_order, size, stride, 0);

            // p = pointer_chasing_16(buffer, 2*len/16);

            p = buffer;
            iter = 0;
            //warmup for 1sec
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &begin);
            do
            {
                iter ++;
                p = pointer_chasing_16(p, len/16);
                clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
            } while (((end.tv_sec - begin.tv_sec)*(1e9) + end.tv_nsec - begin.tv_nsec) < 1e9);

            iter *= 4;

            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &begin);
            p = pointer_chasing_16(p, iter*len/16);
            clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
            //check result, also prevent optimization
            if(p != buffer) printf("run error");
            printf("%05.2lf ", ((end.tv_sec - begin.tv_sec)*(1e9) + end.tv_nsec - begin.tv_nsec)/(iter*len));
        }
        printf("\n");
        fflush(NULL);
    }
    

    return 0;
}
